home *** CD-ROM | disk | FTP | other *** search
/ Amiga Format CD 48 / Amiga Format CD48 (1999-12-13)(Future Publishing)(GB)(Track 1 of 2)[!][issue 2000-01].iso / -in_the_mag- / networking / crosspc / parpc04 / hardware / src / parkern.asm < prev    next >
Assembly Source File  |  1993-09-01  |  29KB  |  996 lines

  1. ; PARKERN.ASM
  2. ;
  3. ; Lowlevel routines for PARnet
  4. ; 03-JUN-93 First PC version <S.A.Pechler@bdk.tue.nl>
  5. ; 03-JUL-93 Fixed bug when transferring odd number of bytes.
  6. ;           Optimized by putting some counters in registers   <S.A.Pechler>
  7. ; 07-JUL-93 Bugfix: ES was not set when reading into overflow buffer.
  8. ;           Deleted leading underscores from all data labels.
  9. ;           Added ParReadV and ParWriteV functions.           <S.A.Pechler>
  10. ; 11-AUG-93 Bugfix: When an error occured in parread(), the errornumber was
  11. ;        set in the wrong register (was BX, had to be AX). <S.A.Pechler>
  12. ; 01-SEP-93 Optimized the functions by using block move operations (STOSB).
  13. ;           Changed the register usage of read_data() and write_data().
  14. ; 02-SEP-93 Rewrote all private routines for usage without the special
  15. ;           PARnet interface (connections between 2 machines only).
  16. ;
  17. ;
  18. ;   PARALLEL PORT NETWORK LOW LEVEL ROUTINES
  19. ;
  20. ;   THE CABLE:
  21. ;                  (2-9)   D7-D0  ----------  D7-D0 (2-9)
  22. ;        PC        ( 1)    STROBE ----------  BUSY  (11)      AMIGA
  23. ;     PARALLEL  <= (14)    AUTOLF ----------  POUT  (12)  => PARALLEL
  24. ;       CARD       (16)    INIT   --+----+--  SEL   (13)       PORT
  25. ;                  (10)    ACK    -/      \-  ACK   (10)
  26. ;                  (18-22) GND    ----------  GND   (18-22)
  27. ;
  28. ;      Cable connections between PC and Amiga WITHOUT(!) an interface
  29. ;
  30. ;
  31. ;
  32. ;                 (2-9)   D7-D0  ----------  D7-D0  (2-9)
  33. ;        PC        ( 1)    STROBE ----------  STROBE ( 1)        PC
  34. ;     PARALLEL  <= (14)    AUTOLF ----------  AUTOLF (14)  => PARALLEL
  35. ;       CARD       (16)    INIT   --+----+--  INIT   (16)       CARD
  36. ;                  (10)    ACK    -/      \-  ACK    (10)
  37. ;                  (18-22) GND    ----------  GND    (18-22)
  38. ;
  39. ;       Cable connections between 2 PC's WITHOUT(!) an interface
  40. ;
  41. ;
  42. ;   Parallel port usage:
  43. ;
  44. ;   DATA PORT   : used for data input & output
  45. ;   CONTROL PORT: used for control input & output
  46. ;
  47. ;   All lines pulled up.  Thus, asserted state is a 0.    Idle
  48. ;   state is an undriven (1).  Protocol transfers a byte at
  49. ;   a time.  Protocol is ethernet style with a small window
  50. ;   of error in the line aquisition routine.
  51. ;
  52. ;   Note:   Timeouts should be set around a second.  Ideally
  53. ;        defaults should be fixed on faster machines.
  54. ;
  55. ;   LINE description:
  56. ;
  57. ;   PARnet: Cable: PC:      Description:
  58. ;   ---------------------------------------------------------
  59. ;   ~ACK    BUSY   STROBE   hand shake
  60. ;   ~REQ    POUT   AUTOLF   hand shake
  61. ;    CTL    SEL    INIT     1 = special byte, 0 = data byte.
  62. ;                (for address mark, valid when ~REQ
  63. ;                 goes low.    For EOP mark, sample
  64. ;                 when ~REQ goes high)
  65. ;
  66. ;   See the associated routines for the exact low-level protocols.
  67. ;
  68. ;
  69. ;   PROTOCOL DESCRIPTION:
  70. ;
  71. ;   HandShake    (Reader <- Writer transfer).  A Handshake
  72. ;        sequence transfers TWO bytes of information.
  73. ;
  74. ;        WRITER                READER
  75. ;    |-> place data, ~REQ->0
  76. ;    |                 wait for ~REQ->0
  77. ;    |                 read data & store
  78. ;    |                 set ~ACK->0
  79. ;    |   wait ~ACK->0
  80. ;    |   place data, ~REQ->1
  81. ;    |                 wait for ~REQ->1
  82. ;    |                 read data & store
  83. ;    |                 set ~ACK->1
  84. ;    |   wait ~ACK->1
  85. ;    |<- LOOP  (2 bytes written)      LOOP  (2 bytes read)
  86. ;
  87. ;
  88. ;   Read:   (1) Determine if your machine is being addressed
  89. ;        (~REQ=0, data=myaddress, CTL=1)
  90. ;        (1) set ~ACK to output and
  91. ;        (2) Handshake sequence for the address mark, only
  92. ;        first byte valid.
  93. ;        (3) Handshake sequence for data util rcv byte
  94. ;        with CTL = 1 (EOP), byte must == 0.
  95. ;        (4) Set ~ACK to input
  96. ;
  97. ;   Write:  (1) AQUIRE THE NETWORK (see below)
  98. ;        involves gaining control and then setting the
  99. ;        CTL and ~REQ to outputs.
  100. ;
  101. ;        Also checks if somebody is writing to us,
  102. ;        in which case -2 is returned instantaniously
  103. ;        indicating we should do a ParRead().
  104. ;
  105. ;        (2) Handshake sequence for address mark, send dest
  106. ;        address (second byte garbage).  Note that CTL->1
  107. ;        *BEFORE* we set ~REQ->0
  108. ;        (3) Handshake sequence for data bytes
  109. ;        (4) Handshake sequence for EOP mark (Note that CTL->1
  110. ;        *AFTER* we get the ~ACK->1 and before release
  111. ;        ~REQ (->1).  Only firstbyte valid and set to 0.
  112. ;
  113. ;        (5) Set ~ACK and ~CTL to inputs
  114. ;
  115. ;   AQUIRE: Line aquisition prevents two people from writing to
  116. ;        the net at the same time.
  117. ;
  118. ;        * A line is considered aquired if ANY of the 3
  119. ;          control lines is 0.
  120. ;        * If network is not aquired:
  121. ;        - set ~ACK to output and 1
  122. ;        - bclr ~ACK to 0 and bne success
  123. ;          (else set ~ACK to input and try again)
  124. ;        - set data lines to output and place my address
  125. ;          (Must ]be done before CTL is glitched)
  126. ;        - set ~CTL to output and 1
  127. ;        - set CTL to 0 and then 1 (glitch it) to cause
  128. ;          FLAG interrupt on all other machines
  129. ;        - set ~REQ to output and 0 (beginning of handshake
  130. ;          sequence)
  131. ;        - lastly, release ~ACK by setting it to an input
  132. ;
  133. ;          Note that at all times at least one line is a 0
  134. ;          so no other machine will attempt to aquire the net.
  135. ;
  136. ;   Note that the destination address is placed on the data
  137. ;   lines after we have aquired the line but before we glitch
  138. ;   the CTL line to cause an interrupt.  This allows the
  139. ;   other machines to instantaniously determine who is being
  140. ;   addressed.
  141. ;
  142. ;   Note that the CTL line gets glitched at the end of a packet
  143. ;   too for the EOP mark.  In this case there is a 0 on the
  144. ;   data lines so while an interrupt is generated, nobody
  145. ;   thinks they are being addressed.
  146. ;
  147. ;
  148. ;   ASSEMBLING/COMPILING the code:
  149. ;
  150. ;   When assembling, use the following flags (turbo-assembler V2.0):
  151. ;
  152. ;   tasm -mx -t -DMEMMOD=large parkern.asm
  153. ;   option -zi adds full debug information.
  154. ;
  155. ;   When including the routines in C, use the following prototypes:
  156. ;
  157. ;   typedef unsigned short int16;
  158. ;   extern void paraddress(int16,int16);
  159. ;   extern int pardataready(void);
  160. ;   extern int parreadV();
  161. ;   extern int parread(unsigned char *,int16);
  162. ;   extern int parwriteV();
  163. ;   extern int parwrite(int16, unsigned char *,int16);
  164. ;
  165.  
  166.     .MODEL MEMMOD,C
  167.     LOCALS
  168.     %MACS
  169.     .LALL
  170.  
  171.     .DATA
  172. ParNetAddr    db 1        ; my PARnet address (default)
  173. LPTData        dw 0378h    ; LPT data port address (default)
  174. LPTStatus    dw 0379h    ; LPT status port address (default)
  175. LPTControl    dw 037ah    ; LPT control port address (default)
  176.  
  177. ParLLTimeout    dw 65535
  178.     ;should be dl 983025    ; default timeout value (about 1 second?)
  179. DestAddress    db 5        ; Destination address for write
  180. Dummybuf    db 0        ; dummy buffer
  181.         db 0        ; dummy buffer
  182.         db 0,0        ; padding
  183. NextRVector     db 0            ; Flag: vector to next read buffer present
  184. NextWVector    db 0        ; Flag: vector to next write buffer present
  185.  
  186.     .CODE
  187.         PUBLIC paraddress,pardataready,parreadV,parread,parwrite,parwriteV
  188.  
  189. ;============================================================================
  190. ;Private Routines
  191. ;
  192. ;Don't use these in any program.
  193.  
  194.  
  195. ;----------------------------------------------------------------------------
  196. ; Stable
  197. ;
  198. ; Put the interface in a stable mode by setting the data and control lines
  199. ; to high.
  200. ;
  201. stable:
  202.     push ax
  203.     push dx
  204.     mov dx,LPTData      ;data register
  205.     mov al,-1      ;datalines high
  206.     out dx,al      ;set it
  207.     mov dx,LPTControl ;Control register
  208.     mov al,14h      ;all outputs to 1 (INIT is not inverted!), enable IRQ7
  209.     out dx,al      ;write control
  210.     pop dx
  211.     pop ax
  212.     ret
  213.  
  214. ;----------------------------------------------------------------------------
  215. ;Data Input
  216. ;
  217. ; Set the datalines to 'input' by setting all lines to high
  218. ;
  219. data_input:
  220.     push ax
  221.     push dx
  222.     mov dx,LPTData    ;data register
  223.     mov al,-1    ;datalines high
  224.     out dx,al    ;set it
  225.     pop dx
  226.     pop ax
  227.     ret
  228.  
  229. ;----------------------------------------------------------------------------
  230. ; Control Input
  231. ;
  232. ; Set the control-lines to 'input' by setting all lines to high
  233. ;
  234. ;control_input:
  235. ;    push ax
  236. ;    push dx
  237. ;    mov dx,LPTControl ;Control register
  238. ;    mov al,14h      ;all outputs to 1 (INIT is not inverted!), enable IRQ7
  239. ;    out dx,al      ;set it
  240. ;    pop dx
  241. ;    pop ax
  242. ;    ret
  243.  
  244. ;----------------------------------------------------------------------------
  245. ; Read Data
  246. ;
  247. ; read data from the cable, put it in AL
  248. ;
  249. read_data:        ;data line must be set to INPUT first!
  250.     push dx
  251.     mov dx,LPTData    ;data register
  252.     in al,dx    ;read it
  253.     pop dx
  254.     ret
  255.  
  256. ;----------------------------------------------------------------------------
  257. ; Read Control
  258. ;
  259. ; Read the controlbits from the cable (busy, pout & sel)
  260. ;
  261. ; BL: control read from control register, represents the REAL line
  262. ;     status (so if a line is +5V, then the bit is 1).
  263. ;
  264. ; BL bits: 7654 3210  centronics:    cable:  parnet:
  265. ; value:   XXXX X100 
  266. ;          |||| |||`- strobe          busy    ack
  267. ;          |||| ||`-- auto LF         pout    req
  268. ;          |||| |`--- init            sel     ctl
  269. ;          |||| `---- selin            -       -
  270. ;          ````------ invalid          -       -
  271. ;
  272. read_control:            ;control lines must be set to input first!
  273.     push ax
  274.     push dx
  275.     mov dx,LPTControl    ;Control register
  276.     in al,dx        ;read it
  277.     xor al,0bh        ;invert all but init
  278.     and al,07        ;discard bits not needed
  279.     mov bl,al        ;return value in BL
  280.     pop dx
  281.     pop ax
  282.     ret            ;ready, controlbits in BL
  283.  
  284. ;-----------------------------------------------------------------------------
  285. ;Clear ACK Only
  286. ;
  287. ; Clear the parnet ack-bit. Leave other control lines high.
  288. ; Warning: I can't read my own control-lines back!
  289. ;
  290. clear_ack_only:
  291.     push bx
  292.     mov bl,0feh        ; set parnet ack-bit to 0
  293.     call write_control    ; write control
  294.     pop bx
  295.     ret
  296.  
  297. ;----------------------------------------------------------------------------
  298. ;Set ACK All
  299. ;
  300. ; Set the parnet ack-bit. Leave other control lines high.
  301. ; Warning: I can't read my own control-lines back!
  302. ;
  303. Set_ack_all:
  304.     push bx
  305.     mov bl,0fh        ; set all bits to 1 (including ACK)
  306.     call write_control    ; write control
  307.     pop bx
  308.     ret
  309.  
  310. ;----------------------------------------------------------------------------
  311. ; Write control
  312. ;
  313. ; BL: controlbits to be written
  314. ;
  315. ; BL bits: 7654 3210  centronics
  316. ; value:   XXXX X100  keyword:   cable:  controlbits:
  317. ;          |||| |||`- strobe      busy      ack
  318. ;          |||| ||`-- auto LF     pout      req
  319. ;          |||| |`--- init        sel       ctl
  320. ;          |||| `---- selin        -         -
  321. ;          ````------ invalid      -         -
  322. ;
  323. ; The value in BL represents the REAL LINE STATUS, so a bit=0 means the
  324. ; line is on low voltage.
  325. ;
  326. ; The INIT output is on the parallel card not inverted. This bit will be
  327. ; inverted in this procedure, so you don't have to care about it.
  328. ;
  329. write_control:
  330.     push ax
  331.     push dx
  332.     mov dx,LPTControl ; control register
  333.     mov al,bl      ; move control bits to AL (for OUT-instruction)
  334.     xor al,0bh      ; invert all lines except for INIT (=ctl)
  335.     out dx,al      ; write control
  336.     pop dx
  337.     pop ax
  338.     ret
  339.  
  340. ;----------------------------------------------------------------------------
  341. ; Write DATA
  342. ;
  343. ; Put Data on the line.
  344. ;
  345. ; AL: data to be written.
  346. ;
  347. write_data:            ; interface must be in stable mode!
  348.     push dx
  349.     mov dx,LPTData        ; data register
  350.     out dx,al        ; put data on line
  351.     pop dx
  352.     ret
  353.  
  354. ;============================================================================
  355. ;Public routines
  356.  
  357.  
  358. ; (void) paraddress(int16 myaddr,int16 LPTAddress)
  359. ;
  360. ; Set my ParNet address (1-254) and LPT port address (0378h,03bch or 0278h)
  361. ;
  362. ; ParNet addresses 0 and 255 are reserved!
  363. ;
  364. paraddress    PROC
  365.         ARG myad:word,lptad:word
  366.  
  367.     mov ax,myad        ; my parnet address
  368.     mov ParNetAddr,al    ; store address
  369.     mov ax,lptad        ; LPT port address
  370.     mov LPTData,ax        ; Place data port address
  371.     inc ax            ; next register is status port
  372.     mov LPTStatus,ax
  373.     inc ax            ; next register is control port
  374.     mov LPTControl,ax
  375.     call stable        ; all lines high
  376.     ret
  377.  
  378. paraddress     ENDP
  379.  
  380. ; int = pardataready(void)
  381. ;
  382. ; Check for data present (e.g. after an IRQ7).
  383. ;
  384. ; Returns:  1 if packet is probably pending for you
  385. ;           0 if line is currently idle
  386. ;          -1 if packet isn't for you
  387. ;
  388. ; If line has been aquired but no control address has been
  389. ; put on it yet, pardataready() will wait for a control
  390. ; address.  Thus, after a signal, a single call to
  391. ; pardataready() should suffice.
  392. ;
  393. pardataready    PROC
  394.     call stable        ; be sure all lines are set to input
  395. .pdstable:
  396.     call read_control    ; read control in BL
  397.     mov cl,bl        ; save it
  398.     call read_data        ; read data in AL
  399.     call read_control    ; read control in BL
  400.     cmp cl,bl
  401.     jne .pdstable
  402.  
  403.     ;   Now, pardataready might be called after the sending machine
  404.     ;   has aquired but before it can assert REQ.  However, the
  405.     ;   sending machine has already (guarenteed) placed its address
  406.     ;   on the data port.  So while the address matches, loop while
  407.     ;   REQ not asserted.
  408.  
  409.     test bl,02        ; ~REQ asserted?
  410.     jz .pd10        ; yes
  411.     cmp  [ParNetAddr],al    ; no, does data match anyway?
  412.     je .pdstable        ; YES, loop until get ~REQ or
  413.     jmp short .pdfail    ; data bad.
  414.  
  415. .pd10:
  416.     test bl,04        ; ~REQ is asserted, CTL=1 ?
  417.     jz .pdrn        ; no, middle of some packet
  418.     cmp [ParNetAddr],al    ; yes, my address?
  419.     jne .pdrn        ; nope
  420.     mov ax,1        ; yes, packet (probably) for us
  421.     jmp short .pdend
  422.  
  423. .pdfail:
  424.     test bl,04        ; fail due to ~REQ not asserted
  425.     jz .pdrn        ; CTL=0, line busy
  426.     xor ax,ax        ; line idle.
  427.     jmp short .pdend
  428.  
  429. .pdrn:
  430.     mov ax,-1        ; line busy, packet not for me
  431. .pdend:
  432.     ret
  433.  
  434. pardataready    ENDP
  435.  
  436. ;n = parreadV(unsigned char *buf,int16 bytes, buf2,bytes2, ..., NULL, NULL)
  437. ;n = parread(unsigned char *buf,int16 bytes)
  438. ;
  439. ;Read a pending packet.
  440. ;
  441. ;Returns: n = -1 (1 second timeout, no packet pending)
  442. ;       n = 0 to bytes-1 (1 second timeout after transmission interrupted)
  443. ;      n = bytes (success), or n > bytes (transmitting machine's packet
  444. ;          was larger than we can handle, extra bytes thrown out)
  445. ;
  446. ;NOTE:    Requesting an odd number of bytes is O.K. but if you request N where
  447. ;    N is odd and the writer sends N + 1 you will never know (N will be
  448. ;    returned). See also parwrite() below.
  449. ;
  450.  
  451. parreadV PROC
  452.      ARG buf:ptr, byts:word, nextbuf: ptr, nextbyts:word
  453.  
  454.          mov al,1
  455.          mov NextRVector,al      ; secondary buffer(s) present
  456.          jmp short parRbegin
  457.  
  458. parreadV ENDP
  459.  
  460. parread    PROC
  461.         ARG buf:ptr, byts:word, nbuf:ptr, nextbyts:word
  462.                                 ; ^these fake params are needed for reference.
  463.         mov al,0                ; no secondary buffer(s)
  464.         mov NextRVector,al
  465.  
  466. parRbegin:
  467.     if    @Datasize NE 0
  468.                 push ds         ; the 'USES' macro doesn't work here
  469.                 push es
  470.         push si
  471.         push di
  472.                 les     di,buf  ; es:di = buf, my data segment needs to
  473.                                 ; stay intact.
  474.     else
  475.                 push ds
  476.                 push es
  477.         push si
  478.         push di
  479.                 mov ax,ds
  480.                 mov es,ax       ; to simulate a mov es,ds
  481.                 mov di,buf      ; es:di = buf (ds already set)
  482.     endif
  483.  
  484.     cld            ; all moves will be forward
  485.     mov si,byts        ; maximum number of bytes to read
  486.     call stable        ; ensure line not asserted
  487.     mov DX,[ParLLTimeout]    ; DX = timeout load
  488.  
  489.     ;  Wait loop for address mark
  490.     ;  Ctl = 1, ~DReq = 0
  491. .rmstab:
  492.     call read_control    ; read control in BL
  493.     mov cl,bl        ; save it
  494.     call read_data        ; read data in AL
  495.     call read_control    ; read control in BL
  496.     cmp cl,bl        ; control lines stable?
  497.     jne .rmstab        ; nope
  498.     test bl,04        ; expect CTL=1
  499.     jz .rms1        ; nope
  500.     test bl,02        ; expect ~REQ=0
  501.     jz .rms2        ; yes
  502.  
  503. .rms1:
  504.     dec dx            ; decrement timeout count
  505.     jnz .rmstab        ; no timeout.
  506.     mov ax,-1        ; return value: read timeout
  507.     jmp .rmend        ; no address mark!
  508.  
  509. .rms2:
  510.     cmp [ParNetAddr],al    ; My address?
  511.     jne .rms1        ; no, timeout loop
  512.  
  513.     ; Got my address, ~Ack byte.
  514.  
  515.     mov DX,[ParLLTimeout]    ; reset timeout
  516.     call Clear_Ack_Only    ; set ~Ack to 0
  517.  
  518. .rms4:  call read_control    ; get control
  519.     test bl,02        ; wait for ~REQ to go away
  520.     jnz .rms5
  521.     dec dx            ; decrement timeout count
  522.     jnz .rms4
  523.     mov ax,-2        ;~REQ not released?
  524.     jmp .rmend
  525.  
  526. .rms5:  call Set_Ack_All    ; release ~ACK
  527.  
  528.     mov cx,0        ; set # of bytes read to 0
  529.     jmp .rms10        ; skip past move
  530.  
  531.  
  532.     ; MAIN READ LOOP
  533.     ;
  534.     ; cx holds count (number of bytes read).
  535.     ; DI buffer pointer (place to store data).
  536.  
  537. .rms10loop:
  538. ;    mov es:[di],al        ; store data
  539. ;    inc di            ; next address
  540.     stosb
  541.  
  542. .rms10:
  543.     call Read_Control
  544.     test bl,02        ; wait for ~REQ asserted
  545.     jz .rms20
  546.     call Read_control
  547.     test bl,02        ; again
  548.     jz .rms20
  549.     mov DX,[ParLLTimeout]    ; reset timeout
  550.  
  551. .rms11: call Read_Control
  552.     test bl,02        ; wait for ~REQ asserted with timeout
  553.     jz .rms20
  554.     dec dx            ; decrement timeout count
  555.     jnz .rms11
  556.     mov ax,-1
  557.     jmp short .rmend    ; timeout
  558.  
  559. .rms20: call read_data        ; get data in al and
  560.     call Clear_Ack_Only    ; assert ~ACK
  561.  
  562.     ; note on CTL = 1 end sequence this data item is a dummy
  563.  
  564. ;    mov es:[di],al          ; store data
  565. ;    inc di            ; next address
  566.     stosb
  567.  
  568.     inc cx            ; optimized, but not quite true,
  569.     inc cx            ; we've only written one 1 sf
  570.  
  571.     call Read_Control
  572.     test bl,02        ; wait for ~REQ released
  573.     jnz .rms30
  574.     call Read_Control
  575.     test bl,02        ; again
  576.     jnz .rms30
  577.     mov DX,[ParLLTimeout]    ; reset timeout
  578. .rms21:
  579.     call Read_Control
  580.     test bl,02        ; wait for ~REQ released with timeout
  581.     jnz .rms30
  582.     dec dx            ; decrement timeout count
  583.     jnz .rms21        ; no timeout yet?
  584.     jmp short .rmendsub    ; sub because CX is 2 ahead
  585.  
  586.  
  587. .rms30:
  588.     call read_data        ; get data in al
  589.     call Read_Control    ; get CTL status in BL
  590.     call Set_Ack_All    ; release ~ACK    
  591.     test bl,04        ; EOP if CTL=1
  592.     jnz .rmeop
  593.  
  594.     ; CANNOT STORE DATA HERE! In case odd # bytes requested,
  595.     ; second byte would overflow buffer (each handshake sequence ALWAYS
  596.     ; transfers 2 bytes of information).
  597.  
  598.     dec  si            ; # of bytes remaining
  599.     jz   .rmodd        ; already zero, # of bytes were odd
  600.     dec  si
  601.  
  602.     jz  .rmste        ; reached zero (even bytes)
  603.     jmp .rms10loop        ; continue if >0.
  604.  
  605. .rmodd: dec si            ; si must be -1 when odd # bytes requested
  606.     dec cx            ; fixup count (was one ahead)
  607.     jmp short .rmeven
  608.  
  609. .rmste:
  610.         mov es:[di],al        ; if si = 0, its's even and we
  611.                 ; should store the last byte.
  612. .rmeven:
  613.         cmp NextRVector,0        ; if next buffer does not exist
  614.         je  .rmovflow           ; overflow
  615. .rmsev0:
  616.         cmp WORD PTR nbuf,0     ; next buffer points to somewhere?
  617.        if @Datasize NE 0
  618.         jne .rmsev1
  619.         cmp WORD PTR nbuf+2,0   ; check also the segment
  620.        else
  621.         je .rmovflow
  622.        endif
  623. .rmsev1:
  624.        mov si,nextbyts          ; length of next buffer
  625.        if @Datasize NE 0
  626.         les di,nbuf             ; load pointer to next buffer
  627.         add bp,6                ; this is VERY tricky  = sizeof(nbuf+nbyts)
  628.        else
  629.         mov di,nbuf             ; load pointer to next buffer
  630.         add bp,4                ; this one is also VERY tricky
  631.        endif
  632.         or si,si                ; size of this buffer = 0 ?
  633.         jz .rmsev0              ; yes, then check for next buffer
  634.         jmp .rms10              ; proceed reading
  635.  
  636. .rmovflow:
  637.         if @Datasize NE 0
  638.          mov di,ds
  639.          mov es,di              ; to simulate move es,ds
  640.         endif
  641.  
  642.     mov di,OFFSET Dummybuf    ; overflow, use dummy buffer
  643.  
  644.     jmp .rms10
  645.  
  646. .rmeop:
  647.     cmp al,0        ; EOP data better be 0!
  648.     je .rmendsub
  649.     mov ax,-3        ; bad protocol
  650.     jmp short .rmend
  651.  
  652. .rmendsub:
  653.     dec cx            ; because we were two ahead
  654.     dec cx
  655.         mov ax,cx        ; return value in AX
  656. .rmend:
  657.     call data_input
  658.     call Set_Ack_All    ; setting ~ACK to input
  659.     call stable
  660.     pop di
  661.     pop si
  662.         pop es                  ; normally the 'USES' macro inserts these
  663.         pop ds
  664.     ret            ; return value in AX
  665.  
  666. parread    ENDP
  667.  
  668. ;n = parwriteV(int 16 destaddr, unsigned char *buf, int16 bytes,
  669. ;              buf2, bytes2, ..., NULL, NULL)
  670. ;n = parwrite(int16 destaddr, unsigned char *buf, int16 bytes)
  671. ;
  672. ;Write a packet.
  673. ;
  674. ;Returns: n = -2    Cannot write anything, a packet is pending
  675. ;            (instantanious)
  676. ;      n = -1    Destination machine does not respond (1 sec timeout)
  677. ;      n = N        N bytes written ok (success if n == bytes)
  678. ;
  679. ; NOTE: sending an odd number of bytes is O.K. but if you write N where
  680. ;    N is odd and the reader requests N + 1 he will get N + 1 the last
  681. ;    byte being garbage.
  682. ;
  683. parwriteV PROC
  684.       ARG dest:word, buf:ptr, byts:word, nextbuf:ptr, nextbyts:word
  685.  
  686.       mov al,1
  687.       mov NextWVector,al
  688.       jmp short parWbegin
  689.  
  690. parwriteV ENDP
  691.  
  692. parwrite  PROC
  693.       ARG dest:word, buf:ptr, byts:word, nbuf:ptr, nextbyts:word
  694.                 ;         ^these fake params are needed for
  695.                 ;          reference.
  696.       mov al,0
  697.       mov NextWVector,al    ; no secondary buffer(s)
  698.  
  699. parWbegin:
  700.     if @Datasize NE 0
  701.         push ds        ; the 'USES' macro doesn't work here
  702.         push es
  703.         push si
  704.         push di
  705.                 les si,buf      ; es:si = source-buffer to be written
  706.     else
  707.                 push ds
  708.         push es
  709.         push si
  710.         push di
  711.                 mov ax,ds
  712.                 mov es,ax       ; to simulate a mov es,ds
  713.                 mov si,buf      ; es:si = buf (ds already set)
  714.     endif
  715.  
  716.     cld            ; all moves will be forward
  717.     mov ax,dest        ; destination address
  718.     mov DestAddress,al    ; can't do a direct move to DestAddress
  719.     mov di,byts        ; number of bytes to write
  720.  
  721.  
  722.     call stable        ; ensure line not asserted
  723.     mov DX,[ParLLTimeout]    ; DX = timeout load
  724.  
  725. .wmstab:
  726.     cli            ; disable interrupts
  727.     call Read_Control
  728.     mov cl,bl
  729.     call read_data    
  730.     call Read_Control    ; get stable control
  731.     cmp cl,bl
  732.     je .wmstab1
  733.     sti            ; set interrupts
  734.     jmp short .wmstab
  735.  
  736.     ; Interrupts still disabled
  737.     ; BL holds ~ACK ~REQ and CTL status
  738.  
  739. .wmstab1:
  740.     mov ax,-2        ; number of bytes written yet (=none)
  741.     cmp bl,07h        ; ~ACK=1, ~REQ=1, CTL=1 ?
  742.     je .wm02
  743.  
  744.     ; if CTL=1, ~REQ=0 and AL=my address then
  745.     ; return with -2
  746.  
  747.     test bl,02h        ; ~REQ = 0?
  748.     jne .wm01        ; nope
  749.     test bl,04h        ; CTL = 1?
  750.     je .wm01        ; nope
  751.     cmp [ParNetAddr],al    ; somebody is calling me?
  752.     jne .wm01        ; nope
  753.  
  754.     sti            ; enable interrupts
  755.     jmp .wmend
  756.  
  757. .wm01:
  758.     sti            ; enable interrupts
  759.     dec dx            ; decrement timeout count
  760.     jnz .wmstab
  761.     jmp .wmend
  762.  
  763.     ; interrupts still disabled
  764.     ; we almost own the line
  765.  
  766. .wm02:
  767.     call Read_Control
  768.     call Clear_Ack_Only    ; assert ~ACK
  769.     test bl,01
  770.     jne .wm05        ; ACK was released before, have line!
  771.  
  772.     call Set_Ack_All    ; release ~ACK
  773.     jmp short .wm01
  774.  
  775.     ; Line now aquired.
  776.  
  777. .wm05:
  778.     sti            ; enable interrupts
  779.     mov al,DestAddress
  780.     call Write_Data        ; set destination address on datalines
  781.  
  782.     ; Before asserting ~REQ, pulse CTL to cause interrupt on remote
  783.     ; machines. Note that our address is already on the datalines.
  784.  
  785.     mov bl,02h        ; leave ~ACK=0 and ~REQ=1, but CTL->0
  786.     call Write_Control
  787.     mov bl,06h        ; leave ~ACK=0 and ~REQ=1, but CTL->1
  788.     call Write_Control
  789.     mov bl,04h        ; leave ~ACK=0 and CTL=1, but ~REQ->0  
  790.     call Write_Control    ; assert REQ
  791.     mov bl,05h        ; leave CTL=1 and ~REQ=0, but ~ACK->1
  792.     call Write_Control    ; release ACK
  793.                 ; (note that REQ->0 before ACK->released)
  794.  
  795.     mov ax,-1        ; number of bytes written yet (none)
  796.  
  797.     ; interrupts enabled for transfer (fully handshaked)
  798.     ;
  799.     ; Address mark ~ACK, wait for ~ACK asserted.
  800. .wm10:
  801.     call Read_Control
  802.     test bl,01        ; ~ACK asserted?
  803.     jz .wm15        ; yes, remote machine got my address mark
  804.     mov DX,[ParLLTimeout]    ; DX = timeout count
  805. .wm11:
  806.     call Read_Control
  807.     test bl,01
  808.     jz .wm15
  809.     dec dx            ; decrement timeout count
  810.     jnz .wm11        ; timeout?
  811.     jmp .wmend        ; yes.
  812.  
  813.     ; got ack, now set CTL = 0 (leaves at least one line 0 so
  814.     ; nobody else thinks the bus is idle!)
  815.     ;
  816.     ; note:  Since this is the address mark, and is sampled by
  817.     ;     the reader before it asserts ~ACK, I can set CTL
  818.     ;     = 0 now instead of waiting till after ~ACK is
  819.     ;     released.
  820.  
  821. .wm15:
  822.     mov bl,01h        ; leave ACK=1, REQ=0, but CTL->0
  823.     call Write_Control    ; set CTL = 0 for duration of packet
  824.     mov bl,03h        ; leave ACK=1, CTL=0, but REQ->1
  825.     call Write_Control    ; release ~REQ
  826.  
  827.     mov cx,0        ; number of bytes written (none)
  828.  
  829.     ; Data transfer loop
  830.     ;
  831.     ; wait for ~ACK to be released (-> 1). If no more bytes to
  832.     ; write, then skip to .wm50
  833.  
  834. .wm20:
  835.     or  di,di        ; more data in this buffer?
  836.     jz .wm50        ; nope
  837.  
  838.     call Read_Control
  839.     test bl,01        ; wait for ~ACK to be released
  840.     jnz .wm30
  841.     mov DX,[ParLLTimeout]    ; DX = timeout countdown
  842. .wm21:
  843.     call Read_Control
  844.     test bl,01
  845.     jnz .wm30        ; need the timeout here?
  846.     call Read_control
  847.     test bl,01        ; check ACK again
  848.     jnz .wm30
  849.     dec dx
  850.     jnz .wm21
  851.     mov ax,cx        ; return value in AX
  852.     jmp .wmend        ; timeout
  853.  
  854.     ; Assert ~REQ for this data byte and wait for ~ACK
  855.  
  856. .wm30:
  857.         mov al,es:[si]        ; get next data byte.
  858.     inc si            ; this can't be done with a LODSB :-(
  859.  
  860.     call Write_Data        ; store data and ..
  861.     mov bl,01h        ; leave ACK=1, CTL=0, but REQ->0
  862.     call Write_Control    ; .. assert ~REQ
  863.  
  864.         mov al,es:[si]          ; get next data bytes
  865.     inc si            ; this can't be done with a LODSB :-(
  866.  
  867.     inc cx            ; number of bytes written (this only)
  868.                 ; (not valid until we get ACK which
  869.                 ;  is why the wmendsub is included)
  870.  
  871.     call Read_Control
  872.     test bl,01        ; wait for ACK asserted
  873.     jz .wm40
  874.     call Read_Control
  875.     test bl,01        ; look again
  876.     jz .wm40
  877.     mov DX,[ParLLTimeout]    ; DX = timeout count
  878. .wm31:
  879.     call Read_Control
  880.     test bl,01        ; wait for ACK asserted with timeout
  881.     jz .wm40
  882.     dec dx            ; decrement timeout count
  883.     jnz .wm31
  884.     jmp short .wmendsub    ; timeout, no bytes written!
  885.  
  886.     ; have ~ACK, so byte transmitted. increment bytes written,
  887.     ; decr. of bytes left was already done before.
  888.     ; now send second byte and loop back.
  889.  
  890. .wm40:    call Write_Data        ; data was loaded in AL before.
  891.     mov bl,03h        ; leave ACK=1, CTL=0, but REQ->1
  892.     call Write_Control    ; release ~REQ
  893.     inc cx            ; increment number of bytes written
  894.  
  895.     dec di            ; one less bytes
  896.     jz .wm40a        ; odd number of bytes were written
  897.     dec di            ; one less bytes
  898.     jz .wm50        ; these were the last even bytes(s)
  899.  
  900.     jmp .wm20        ; loop back
  901.  
  902. .wm40a: dec di            ; fixup, di = -1 when odd bytes requested
  903.     jmp short .wm50        ; last odd byte, go send an EOP
  904.  
  905.     ; Last byte in buffer has been transmitted.
  906.     ;
  907.     ; Get next buffer in vector.
  908.  
  909. .wm50:    cmp NextWVector,0    ; if next buffer does not exist
  910.     je .wm50a        ; yes.
  911.         cmp WORD PTR nbuf,0     ; next buffer points to somewhere?
  912.        if @Datasize NE 0
  913.         jne .wmnext
  914.         cmp WORD PTR nbuf+2,0   ; check also the segment
  915.        endif
  916.         je .wm50a        ; is zero, so no next buffer.
  917. .wmnext:
  918.        mov di,nextbyts          ; length of next buffer
  919.        if @Datasize NE 0
  920.         les si,nbuf             ; load pointer to next buffer
  921.         add bp,6                ; this is VERY tricky  = sizeof(nbuf+nbyts)
  922.        else
  923.         mov si,nbuf             ; load pointer to next buffer
  924.         add bp,4                ; this one is also VERY tricky
  925.        endif
  926.     jmp .wm20        ; proceed write.
  927.  
  928. .wm50a:
  929.     ;   Last byte has been transmitted,
  930.     ;
  931.     ;   Wait for ~ACK to be released and then assert ~REQ with
  932.     ;   EOP & CTL = 1
  933.     ;
  934.     ;   (timing on read is that CTL is sampled when ~REQ is
  935.     ;   RELEASED so no timing window here)
  936.  
  937.     call Read_Control    ; Wait ~ACK released
  938.     test bl,01
  939.     jz .wm50
  940.  
  941.     mov al,0
  942.     call Write_Data        ; EOP mark (=0)
  943.     mov bl,01h        ; leave ACK=1 and CTL=0, but REQ->0
  944.     call Write_Control    ; assert ~REQ
  945.  
  946.     ; Wait for ~ACK asserted
  947.  
  948.     call Read_Control
  949.     test bl,01        ; ACK asserted?
  950.     jz .wm60        ; yes
  951.     mov DX,[ParLLTimeout]    ; DX = timeout count
  952. .wm51:
  953.     call Read_Control
  954.     test bl,01        ; wait for ACK asserted with timeout
  955.     jz .wm60
  956.     dec dx            ; decrement timeout count
  957.     jnz .wm51
  958.     mov ax,-3        ; EOP failed
  959.     jmp short .wmend
  960.  
  961.     ; Set CTL=1 then release ~REQ, then wait for ~ACK released
  962. .wm60:
  963.     mov bl,05h        ; leave ACK=1, REQ=0, but CTL->1
  964.     call Write_Control    ; release CTL
  965.     mov bl,07h        ; leave ACK=1, CTL=1, but REQ->1
  966.     call Write_Control    ; release ~REQ
  967.     
  968.     ; Wait ~ACK released ?
  969.  
  970. .wm61:    call Read_Control
  971.     test bl,01
  972.     jz .wm61
  973.  
  974.     ; Add DI to CX. This handles fixup if an odd number of bytes were
  975.     ; requested written, DI will be -1 (odd) or 0 (even) and CX will 
  976.     ; be one too large (odd) or perfect (even)
  977.  
  978.     add cx,di
  979.     jmp short .wmend1
  980.  
  981. .wmendsub:
  982.     dec cx            ; was ahead in count
  983. .wmend1:
  984.     mov ax,cx        ; return value in AX
  985. .wmend:
  986.     call stable        ; set all lines to input
  987.     pop di
  988.     pop si
  989.     pop es
  990.     pop ds            ; normally done by the 'USES' macro
  991.     ret            ; return value in AX
  992.  
  993. parwrite    ENDP
  994.  
  995.     END
  996.